package com.idempotent.xgfidempotent.config.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.idempotent.xgfidempotent.config.ext.ExtApiIdempotent;
import com.idempotent.xgfidempotent.config.ext.ExtApiToken;
import com.idempotent.xgfidempotent.factory.ExtApiIdempotentFactory;
import com.idempotent.xgfidempotent.factory.IdempotentInterface;
import com.idempotent.xgfidempotent.util.ConstantUtils;
import com.idempotent.xgfidempotent.util.RedisTokenUtils;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Component
@Slf4j
@ConfigurationProperties
public class ExtApiAopIdempotent {
	
	@Value("${xgf.idempotent.derail}")
	private boolean onOff;
	@Autowired
	RedisTokenUtils redisToken;
	

	// 1.使用AOP环绕通知拦截所有访问（controller）
	@Pointcut("@annotation(com.idempotent.xgfidempotent.config.ext.ExtApiToken)")
	public void extApiTokenAop() {
	}
	
	@Pointcut("@annotation(com.idempotent.xgfidempotent.config.ext.ExtApiIdempotent)")
	public void extApiIdempotentAop() {
	}

	// 前置通知
	@AfterReturning(value  = "extApiTokenAop()",argNames = "retVal",returning = "retVal")
	public void afterReturning(JoinPoint point,Object retVal) {
		if(!onOff) {
			return;
		}
		log.info("extApiTokenAop afterReturning");
		MethodSignature signature = (MethodSignature) point.getSignature();
		ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class);
		if (extApiToken != null) {
			// 可以放入到AOP代码 前置通知

			List<IdempotentInterface> list = ExtApiIdempotentFactory.list;
			for (IdempotentInterface idempotentInterface : list) {
				if(idempotentInterface.isFit(retVal)) {
					idempotentInterface.execute(retVal);
					return;
				}
			}
		}
	}

	// 环绕通知
	@Around("extApiIdempotentAop()")
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		if(!onOff) {
			Object proceed = proceedingJoinPoint.proceed();
			return proceed;
		}
		log.info("extApiIdempotentAop around");
		// 2.判断方法上是否有加ExtApiIdempotent
		MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
		ExtApiIdempotent declaredAnnotation = methodSignature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
			String type = declaredAnnotation.type();
			// 如何使用Token 解决幂等性
			// 步骤：
			String token = null;
			HttpServletRequest request = getRequest();
			if (type.equals(ConstantUtils.EXTAPIHEAD)) {
				token = request.getHeader("token");
			} else {
				token = request.getParameter("token");
			}

			if (StringUtils.isEmpty(token)) {
				return "token参数错误";
			}
			// 3.接口获取对应的令牌,如果能够获取该(从redis获取令牌)令牌(将当前令牌删除掉) 就直接执行该访问的业务逻辑
			boolean isToken = redisToken.findToken(token);
			// 4.接口获取对应的令牌,如果获取不到该令牌 直接返回请勿重复提交
			if (!isToken) {
				response("请勿重复提交!");
				// 后面方法不在继续执行
				return null;
			}

		// 放行
		Object proceed = proceedingJoinPoint.proceed();
		return proceed;
	}

	public HttpServletRequest getRequest() {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		return request;
	}

	public void response(String msg) throws IOException {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletResponse response = attributes.getResponse();
		response.setHeader("Content-type", "text/html;charset=UTF-8");
		PrintWriter writer = response.getWriter();
		try {
			writer.println(msg);
		} catch (Exception e) {
			log.error(e.toString());
		} finally {
			writer.close();
		}

	}

}
